home *** CD-ROM | disk | FTP | other *** search
- Subject: v21i002: Apply a filter to a file, safely
- Newsgroups: comp.sources.unix
- Sender: sources
- Approved: rsalz@uunet.UU.NET
-
- Submitted-by: Dan Bernstein <brnstnd@stealth.acf.nyu.edu>
- Posting-number: Volume 21, Issue 2
- Archive-name: filterfile
-
- [ Everyone probably has one of these in their toolkit, although not
- with such extensive copyright, warranty and version numbers; at
- any rate, it's worth having an archived one. /r$ ]
-
- filterfile applies a filter to a file, writing the result to another
- file. Various options let filterfile delete the input file, set the times
- of the output to the times of the input, or set the protection mode of the
- output to that of the input.
-
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then unpack
- # it by saving it into a file and typing "sh file". To overwrite existing
- # files, type "sh file -c". You can also feed this as standard input via
- # unshar, or by typing "sh <file", e.g.. If this archive is complete, you
- # will see the following message at the end:
- # "End of shell archive."
- # Contents: README CHANGES filterfile.man filterfile.c Makefile
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- if test -f 'README' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'README'\"
- else
- echo shar: Extracting \"'README'\" \(738 characters\)
- sed "s/^X//" >'README' <<'END_OF_FILE'
- Xfilterfile - apply a filter to a file
- X
- Xfilterfile version 1.201, October 28, 1989.
- XCopyright (c) 1989, Daniel J. Bernstein.
- XAll rights reserved.
- X
- XThis distribution packaged October 28, 1989.
- X
- XFiles:
- XCHANGES Description of changes since first distributed version
- XREADME This document
- XMakefile Installation commands
- Xfilterfile.c The program
- Xfilterfile.man Documentation
- X
- XEdit the options in Makefile and type make. filterfile will be the
- Xexecutable program; filterfile.1 will be the nroff'ed manual.
- X
- XI don't pretend to know your machine's setup so there's no make install.
- X
- XRead CHANGES for a list of changes. Type filterfile -C and filterfile -W
- Xfor copyright and warranty information, filterfile -H for help.
- END_OF_FILE
- if test 738 -ne `wc -c <'README'`; then
- echo shar: \"'README'\" unpacked with wrong size!
- fi
- # end of 'README'
- fi
- if test -f 'CHANGES' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'CHANGES'\"
- else
- echo shar: Extracting \"'CHANGES'\" \(312 characters\)
- sed "s/^X//" >'CHANGES' <<'END_OF_FILE'
- XVersion 1.201, 10/28/89.
- X
- XChanged filefilter to filterfile at one spot in manual (whoops!).
- XPut missing \n into an error message.
- XPut in new copyright notice and warranty.
- X
- XVersion 1.2.
- X
- XMinor changes.
- XAdded filenames into most of the errors.
- XMade execve into execvp, so filterfile searches paths.
- X
- XVersion 1.0.
- END_OF_FILE
- if test 312 -ne `wc -c <'CHANGES'`; then
- echo shar: \"'CHANGES'\" unpacked with wrong size!
- fi
- # end of 'CHANGES'
- fi
- if test -f 'filterfile.man' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'filterfile.man'\"
- else
- echo shar: Extracting \"'filterfile.man'\" \(4968 characters\)
- sed "s/^X//" >'filterfile.man' <<'END_OF_FILE'
- X.TH filterfile 1
- X.SH NAME
- Xfilterfile \- apply a filter to a file
- X.SH SYNTAX
- Xfilterfile
- X[
- X\fB\-dDpPtT\fI
- X] [
- X\fB\-o\fIout
- X] [
- X\fB\-e\fIext
- X] [
- X\fB\-ACHUVW\fI
- X]
- Xfile /path/filter
- X[
- Xarg ...
- X]
- X.SH DESCRIPTION
- X\fIfilterfile\fB
- Xapplies a filter to a file,
- Xwriting the result to another file.
- XVarious options let
- X\fIfilterfile\fB
- Xdelete the input file,
- Xset the times of the output to the times of the input,
- Xor set the protection mode of the output to that of the input.
- X.PP
- XOptions
- X.B ACHUVW
- Xprint the authorship notice,
- Xcopyright notice,
- Xhelp notice,
- Xshort usage summary,
- Xversion number,
- Xand warranty information respectively.
- X.PP
- X\fIfilterfile\fB
- Xhas several flags:
- X.TP 12
- X.B -d
- XDelete the input file after the filter runs successfully.
- XThe input file will remain untouched if the filter crashes
- Xor exits with an error indication.
- X.TP
- X.B -D
- XDo not delete the input file. This is the default.
- X.TP
- X.B -p
- XSet the protection modes of the output to the protection modes
- Xof the input.
- X.TP
- X.B -P
- XLeave the output protection modes alone. This is the default.
- X.TP
- X.B -t
- XSet the access and modification times of the output to those
- Xof the input.
- X.TP
- X.B -T
- XLeave the times alone. This is the default.
- X.TP
- X\fB-o\fIout
- XWrite the output on file
- X\fIout\fB.
- XThis overrides
- Xthe
- X.B\-e
- Xoption.
- X.TP
- X\fB-e\fIext
- XWrite the output on
- X\fIfile.ext\fB.
- X.PP
- XIf you specify neither
- X.B\-o
- Xnor
- X\fB\-e\fI,
- X\fIfilterfile\fB
- Xwill look for an environment variable
- X.B FILEFILTEREXT
- Xgiving an extension.
- XIf there is no such variable,
- X\fIfilterfile\fB
- Xwill give the output the same name as the input,
- Xfollowed by a period.
- X.PP
- XIf the extension begins with a period and the input file name
- Xends with that extension already, the extension is deleted from
- Xthe file name rather than added to it. If you really do want
- Xperiods in the extension, type them as slashes.
- X.PP
- XThe options and input file name can come in any order,
- Xbut the second argument not starting with a hyphen
- Xis taken to be the name of the filter.
- XAny further arguments are passed on as options to the filter.
- X.PP
- XYou need not specify a full path name for the filter.
- X.SH DIAGNOSTICS
- XAltogether too many,
- Xthough luckily most are self-explanatory.
- XMost of these messages are followed by a more specific explanation
- Xof the reason for the error; this list does not contain all the
- Xpossible variations.
- X.TP
- X\fIfatal: cannot create output file name: out of memory\fB
- XThere is so little memory available that
- X\fIfilterfile\fB
- Xcannot even put together the input file name and extension
- Xto make the output file name.
- X.TP
- X\fIfatal: will not proceed: input file has extra links\fB
- XYou have asked that the input file be deleted, but it
- Xhas more than one link.
- X.TP
- X\fIwarning: cannot change protections of output file\fB
- XThis should never happen,
- Xunless the file changes to someone else's ownership.
- X.TP
- X\fIfatal: cannot execute filter\fB
- X\fIfilterfile\fB
- Xis unable to execute the filter.
- X.TP
- X\fIfatal: cannot fork\fB
- XYou have probably run out of processes.
- X.TP
- X\fIfatal: cannot reopen input file\fB
- X\fIfilterfile\fB
- Xis unable to open the input file at its original location.
- X.TP
- X\fIfatal: cannot reopen output file\fB
- X\fIfilterfile\fB
- Xis unable to open the output file at its original location.
- X.PP
- XThe following errors should all be self-explanatory:
- X.TP
- X\fIfatal: cannot open input file\fB
- X.TP
- X\fIfatal: cannot create output file\fB
- X.TP
- X\fIfatal: will not delete input file: it has been moved\fB
- X.TP
- X\fIfatal: will not delete input file: it has extra links\fB
- X.TP
- X\fIfatal: cannot delete input file\fB
- X.TP
- X\fIwarning: will not set times of output file: it has been moved\fB
- X.TP
- X\fIwarning: cannot set times of output file\fB
- X.PP
- XThere are several errors that should never happen:
- X.TP
- X\fIfatal: cannot stat input file\fB
- X.TP
- X\fIfatal: cannot stat output file\fB
- X.TP
- X\fIfatal: cannot prepare input\fB
- X.TP
- X\fIfatal: cannot prepare output\fB
- X.TP
- X\fIfatal: can't find filter process\fB
- X.TP
- X\fIfatal: cannot restat input file\fB
- X.TP
- X\fIfatal: cannot restat output file\fB
- X.SH MACHINES
- X\fIfilterfile\fB
- Xhas been tested
- Xon an Astronautics ZS-2
- Xrunning ZSUnix.
- X.SH FILES
- XNone.
- X.SH BUGS
- XNone known.
- X.SH RESTRICTIONS
- X\fIfilterfile\fB
- Xtries its best to detect when a file has been moved,
- Xso that it won't delete the wrong file or change the times of
- Xthe wrong file. It also tries to detect when the input file
- Xhas several links, in which case it does not even try to delete
- Xthe file. Unfortunately, there is a very short period of time
- Xbetween these tests and the actual operations, so
- X\fIfilterfile\fB
- Xcould enter a (non-looping) race condition and act incorrectly.
- XThere is no way to avoid this race with available versions of UNIX.
- X.PP
- XIn other words, if you move the input file out of the way and give
- Xa different file the same name, the moment before
- X\fIfilterfile\fB
- Xremoves that filename,
- Xyou deserve what you get.
- X.SH VERSION
- Xfilterfile version 1.201, dated October 28, 1989.
- X.SH AUTHOR
- XCopyright 1989, Daniel J. Bernstein.
- X.SH "SEE ALSO"
- Xapply(1),
- Xxargs(1)
- END_OF_FILE
- if test 4968 -ne `wc -c <'filterfile.man'`; then
- echo shar: \"'filterfile.man'\" unpacked with wrong size!
- fi
- # end of 'filterfile.man'
- fi
- if test -f 'filterfile.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'filterfile.c'\"
- else
- echo shar: Extracting \"'filterfile.c'\" \(9303 characters\)
- sed "s/^X//" >'filterfile.c' <<'END_OF_FILE'
- X/*
- Xfilterfile.c: Pipe a file through a filter into another file.
- X
- XThis program is an obnoxious example of how to check return codes.
- X*/
- X
- Xstatic char filterfileauthor[] =
- X"filterfile was written by Daniel J. Bernstein.\n\
- XInternet address: brnstnd@acf10.nyu.edu.\n";
- X
- Xstatic char filterfileversion[] =
- X"filterfile version 1.201, October 28, 1989.\n\
- XCopyright (c) 1989, Daniel J. Bernstein.\n\
- XAll rights reserved.\n";
- X
- Xstatic char filterfilecopyright[] =
- X"filterfile version 1.201, October 28, 1989.\n\
- XCopyright (c) 1989, Daniel J. Bernstein.\n\
- XAll rights reserved.\n\
- X\n\
- XYou are granted the following rights: A. To make copies of this work in\n\
- Xoriginal form, so long as (1) the copies are exact and complete; (2) the\n\
- Xcopies include the copyright notice, this paragraph, and the disclaimer\n\
- Xof warranty in their entirety. B. To distribute this work, or copies made\n\
- Xunder the provisions above, so long as (1) this is the original work and\n\
- Xnot a derivative form; (2) you do not charge a fee for copying or for\n\
- Xdistribution; (3) you ensure that the distributed form includes the\n\
- Xcopyright notice, this paragraph, and the disclaimer of warranty in their\n\
- Xentirety. These rights are temporary and revocable upon written, oral, or\n\
- Xother notice by Daniel J. Bernstein. This copyright notice shall be\n\
- Xgoverned by the laws of the state of New York.\n\
- X\n\
- XIf you have questions about filterfile or about this copyright notice,\n\
- Xor if you would like additional rights beyond those granted above,\n\
- Xplease feel free to contact the author at brnstnd@acf10.nyu.edu\n\
- Xon the Internet.\n";
- X
- Xstatic char filterfilewarranty[] =
- X"To the extent permitted by applicable law, Daniel J. Bernstein disclaims\n\
- Xall warranties, explicit or implied, including but not limited to the\n\
- Ximplied warranties of merchantability and fitness for a particular purpose.\n\
- XDaniel J. Bernstein is not and shall not be liable for any damages,\n\
- Xincidental or consequential, arising from the use of this program, even\n\
- Xif you inform him of the possibility of such damages. This disclaimer\n\
- Xshall be governed by the laws of the state of New York.\n\
- X\n\
- XIn other words, use this program at your own risk.\n\
- X\n\
- XIf you have questions about filterfile or about this disclaimer of warranty,\n\
- Xplease feel free to contact the author at brnstnd@acf10.nyu.edu\n\
- Xon the Internet.\n";
- X
- Xstatic char filterfileusage[] =
- X"Usage: filterfile [ -dDpPtTACHUVW ] [ -oout ] [ -eext ]\n\
- X file /path/filter [ arg ... ]\n\
- XHelp: filterfile -H\n";
- X
- Xstatic char filterfilehelp[] =
- X"filterfile applies a filter to a file, placing the output in a new file.\n\
- X\n\
- Xfilterfile -A: print authorship notice\n\
- Xfilterfile -C: print copyright notice\n\
- Xfilterfile -H: print this notice\n\
- Xfilterfile -U: print short usage summary\n\
- Xfilterfile -V: print version number\n\
- Xfilterfile -W: print disclaimer of warranty\n\
- X\n\
- Xfilterfile [ -dDpPtT ] [ -oout ] [ -eext ]\n\
- X file /path/filter [ arg ... ]: apply filter to file\n\
- X -d: delete input file\n\
- X -D: do not delete input file (default)\n\
- X -p: preserve protection of file\n\
- X -P: do not preserve protection of file (default)\n\
- X -t: preserve access and modification times of file\n\
- X -T: do not preserve times (default)\n\
- X -eext: sent output to file.ext, or if ext begins with . remove it from name\n\
- X -oout: send output to out (overrides -e)\n\
- X\n\
- XIf you have questions about or suggestions for filterfile, please feel free\n\
- Xto contact the author, Daniel J. Bernstein, at brnstnd@acf10.nyu.edu\n\
- Xon the Internet.\n";
- X
- X#include <stdio.h>
- X#include <sys/types.h>
- X#include <sys/stat.h>
- X#include <sys/file.h>
- X#ifdef BSD
- X#include <limits.h>
- X#endif
- X#include <sys/wait.h>
- X#include <sys/time.h>
- X
- Xextern char *getenv();
- Xextern char *malloc();
- X
- Xint flagdelete = 0;
- Xint flagprotect = 0;
- Xint flagtimes = 0;
- X
- Xint flagunappend = 0;
- X
- Xchar *fnout = 0;
- Xchar *fnin = 0;
- Xchar **filter = 0;
- Xchar *fnappend = 0;
- X
- Xmain(argc,argv,envp)
- Xint argc;
- Xchar *argv[];
- Xchar *envp[];
- X{
- X char *s;
- X int f;
- X int fdin;
- X int fdout;
- X union wait wstat;
- X struct stat stinold;
- X struct stat stoutold;
- X struct stat stin;
- X struct stat stout;
- X struct timeval tvp[2];
- X
- X fnappend = getenv("FILTERFILEEXT");
- X
- X while (*(++argv))
- X if (**argv == '-')
- X while (*(++(*argv)))
- X switch(**argv)
- X {
- X case 'd': flagdelete = 1; break;
- X case 'D': flagdelete = 0; break;
- X case 'p': flagprotect = 1; break;
- X case 'P': flagprotect = 0; break;
- X case 't': flagtimes = 1; break;
- X case 'T': flagtimes = 0; break;
- X case 'e': fnappend = *argv + 1;
- X while (*(++(*argv))) /* null */ ;
- X --(*argv); break; /* we really want breakbreak here */
- X case 'o': fnout = *argv + 1;
- X while (*(++(*argv))) /* null */ ;
- X --(*argv); break; /* we really want breakbreak here */
- X case 'A': printf(filterfileauthor); exit(0);
- X case 'C': printf(filterfilecopyright); exit(0);
- X case 'V': printf(filterfileversion); exit(0);
- X case 'W': printf(filterfilewarranty); exit(0);
- X case 'H': printf(filterfilehelp); exit(0);
- X case 'U': printf(filterfileusage); exit(0);
- X default: ;
- X }
- X else
- X if (fnin == 0)
- X fnin = *argv;
- X else
- X if (filter == 0)
- X {
- X filter = argv;
- X break;
- X }
- X
- X if ((fnin == 0) || (filter == 0))
- X {
- X printf(filterfileusage); exit(1);
- X }
- X if (fnout == 0)
- X {
- X flagunappend = (*fnappend == '.');
- X for (s = fnappend;*s;s++)
- X if (*s == '/')
- X *s = '.';
- X fnout = malloc((unsigned) (strlen(fnin) + strlen(fnappend) + 2));
- X if (fnout == 0)
- X {
- X fprintf(stderr,"filterfile: fatal: cannot create output file name: \
- Xout of memory\n");
- X exit(1);
- X }
- X (void) strcpy(fnout,fnin);
- X if (flagunappend && (strlen(fnout) > strlen(fnappend)) &&
- X (strcmp(fnout + strlen(fnout) - strlen(fnappend),fnappend) == 0))
- X fnout[strlen(fnout) - strlen(fnappend)] = 0;
- X else
- X {
- X (void) strcat(fnout,".");
- X if (fnappend != NULL)
- X (void) strcat(fnout,fnappend);
- X }
- X }
- X
- X /* Okay. Now we have fnin, fnout, and filter. */
- X
- X fdin = open(fnin,O_RDONLY);
- X if (fdin == -1)
- X { fprintf(stderr,"filterfile: fatal: cannot open input file ");
- X perror(fnin); exit(1); }
- X if (fstat(fdin,&stinold) == -1)
- X { perror("filterfile: fatal: cannot stat input file"); exit(1); }
- X if (flagdelete && (stinold.st_nlink > 1))
- X { fprintf(stderr,"filterfile: fatal: will not proceed: \
- Xinput file %s has extra links\n",fnin); exit(1); }
- X
- X tvp[0].tv_sec = stinold.st_atime;
- X tvp[0].tv_usec = 0;
- X tvp[1].tv_sec = stinold.st_mtime;
- X tvp[1].tv_usec = 0;
- X
- X fdout = open(fnout,O_WRONLY | O_CREAT | O_EXCL,0600);
- X if (fdout == -1)
- X { fprintf(stderr,"filterfile: fatal: cannot create output file ");
- X perror(fnout); exit(1); }
- X if (fstat(fdout,&stoutold) == -1)
- X { perror("filterfile: fatal: cannot stat output file"); exit(1); }
- X
- X if (flagprotect)
- X if (fchmod(fdout,stinold.st_mode & 0777) == -1)
- X perror("filterfile: warning: cannot change protections of output file");
- X
- X if ((f = fork()) == 0) /* child */
- X {
- X if (dup2(fdin,0) == -1)
- X { perror("filterfile: fatal: cannot prepare input"); exit(1); }
- X if (dup2(fdout,1) == -1)
- X { perror("filterfile: fatal: cannot prepare output"); exit(1); }
- X execvp(*filter,filter);
- X fprintf(stderr,"filterfile: fatal: cannot execute filter\n");
- X exit(1);
- X }
- X else if (f == -1) /* fork failed */
- X { perror("filterfile: fatal: cannot fork"); exit(1); }
- X
- X /* parent */
- X close(fdin);
- X close(fdout);
- X if (wait(&wstat) == -1)
- X { perror("filterfile: fatal: can't find filter process"); exit(1); }
- X if (wstat.w_T.w_Termsig)
- X exit(1);
- X if (wstat.w_T.w_Retcode)
- X exit((int) wstat.w_T.w_Retcode);
- X if (wstat.w_T.w_Coredump)
- X exit(1);
- X
- X /* Filter exited happily. */
- X
- X if (flagdelete)
- X {
- X /* Race! Race! Race! */
- X fdin = open(fnin,O_RDONLY);
- X if (fdin == -1)
- X { fprintf(stderr,"filterfile: fatal: cannot reopen input file ");
- X perror(fnin); exit(1); }
- X if (fstat(fdin,&stin) == -1)
- X { fprintf(stderr,"filterfile: fatal: cannot restat input file ");
- X perror(fnin); exit(1); }
- X if ((stin.st_dev != stinold.st_dev) || (stin.st_ino != stinold.st_ino))
- X { fprintf(stderr,"filterfile: fatal: will not delete input file %s: \
- Xit has been moved\n",fnin); exit(1); }
- X if (stin.st_nlink > 1)
- X { fprintf(stderr,"filterfile: fatal: will not delete input file %s: \
- Xit has extra links\n",fnin); exit(1); }
- X /* Race! Race! Race! */
- X if (unlink(fnin) == -1)
- X { fprintf(stderr,"filterfile: fatal: cannot delete input file ");
- X perror(fnin); exit(1); }
- X (void) close(fdin);
- X }
- X
- X if (flagtimes)
- X {
- X /* Race! Race! Race! */
- X fdout = open(fnout,O_RDONLY);
- X if (fdout == -1)
- X { fprintf(stderr,"filterfile: fatal: cannot reopen output file ");
- X perror(fnout); exit(1); }
- X if (fstat(fdout,&stout) == -1)
- X { perror("filterfile: fatal: cannot restat output file"); exit(1); }
- X if ((stout.st_dev != stoutold.st_dev) || (stout.st_ino != stoutold.st_ino))
- X { fprintf(stderr,"filterfile: warning: will not set times of \
- Xoutput file %s: it has been moved\n",fnout); exit(1); }
- X /* Race! Race! Race! */
- X if (utimes(fnout,tvp) == -1)
- X { fprintf(stderr,"filterfile: warning: cannot set times of output file ");
- X perror(fnout); exit(1); }
- X }
- X
- X exit(0);
- X}
- END_OF_FILE
- if test 9303 -ne `wc -c <'filterfile.c'`; then
- echo shar: \"'filterfile.c'\" unpacked with wrong size!
- fi
- # end of 'filterfile.c'
- fi
- if test -f 'Makefile' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'Makefile'\"
- else
- echo shar: Extracting \"'Makefile'\" \(236 characters\)
- sed "s/^X//" >'Makefile' <<'END_OF_FILE'
- XCCOPTS=-O
- XNROFFOPTS=-man
- X
- Xdefault: all
- X
- Xall: filterfile filterfile.1
- X
- Xfilterfile: filterfile.c Makefile
- X cc $(CCOPTS) -o filterfile filterfile.c
- X
- Xfilterfile.1: filterfile.man Makefile
- X nroff $(NROFFOPTS) < filterfile.man > filterfile.1
- END_OF_FILE
- if test 236 -ne `wc -c <'Makefile'`; then
- echo shar: \"'Makefile'\" unpacked with wrong size!
- fi
- # end of 'Makefile'
- fi
- echo shar: End of shell archive.
- exit 0
-
-